package render;

import geom.Geometry;

import java.util.Arrays;

/**
 * triangle primitive not used yet
 * 
 * @author Robbe
 * 
 */

public class Triangle implements Primitive {
	ObjectModel model;
	int[] vertexIndices;
	int[] normalIndices;
	int[] texcoordIndices;
	int facetNormalIndex;
	public boolean SMOOTH_SHADING = false;

	public Triangle(int[] vertexIndices, int[] normalIndices, int[] texcoordIndices, ObjectModel model, int facetNormalIndex) {
		this.vertexIndices = vertexIndices;
		this.normalIndices = normalIndices;
		this.texcoordIndices = texcoordIndices;
		this.model = model;
		this.facetNormalIndex = facetNormalIndex;
	}

	public Material getMaterial() {
		return model.getMaterial();
	}

	public double intersection(double[] x, double[] d) {
		double[] vertices = model.getVertices();
		double[] n = Arrays.copyOfRange(model.getFaceNormals(), facetNormalIndex*3, facetNormalIndex*3+3);
//		System.out.println(facetNormalIndex+" n.x: "+n[0]+" n.y: "+n[1]+" n.z: "+n[2]);
		// get all corners of the triangle
		double[] v1 = Arrays.copyOfRange(vertices, vertexIndices[0]*3, vertexIndices[0]*3+3);
		double[] v2 = Arrays.copyOfRange(vertices, vertexIndices[1]*3, vertexIndices[1]*3+3);
		double[] v3 = Arrays.copyOfRange(vertices, vertexIndices[2]*3, vertexIndices[2]*3+3);
		// ray: r = x + t*d 
		// calculate t distance from startpoint (calculate intersection with plan where triangle lie in)
		double divisor = Geometry.dotProduct(d, n);
		if(Math.abs(divisor) <= 0.000001f) {
			// ray don't intersect plane -> can stop here
			return Double.NaN;
		} else {
			double dividend = Geometry.dotProduct(Geometry.difference(x, v1), n);
			double t = -dividend/divisor;
			// calculate intersection
			double[] s = Geometry.sum(x, Geometry.product(d, t));
			// is intersection in triangle? 
			// check orientation of the triangles: (S,V1,V2), (S,V2,V3), (S,V3,V1)
			double[] s_v1 = Geometry.difference(v1, s); // vector: s->v1 = (v1-s)
			double[] s_v2 = Geometry.difference(v2, s); // vector: s->v2 = (v2-s)
			double[] s_v3 = Geometry.difference(v3, s); // vector: s->v3 = (v3-s)
			// check if all normals shows in same direction 
			double a1 = Geometry.dotProduct(Geometry.crossProduct(s_v1, s_v2), Geometry.crossProduct(s_v2, s_v3));
			double a2 = Geometry.dotProduct(Geometry.crossProduct(s_v1, s_v2), Geometry.crossProduct(s_v3, s_v1));
			double a3 = Geometry.dotProduct(Geometry.crossProduct(s_v2, s_v3), Geometry.crossProduct(s_v3, s_v1));
//			System.out.println("a1: "+a1+" a2: "+a2+" a3: "+a3);
			if((a1 > 0 && a2 > 0 && a3 > 0) || (a1 < 0 && a2 < 0 && a3 < 0)) {
				// all normals show in same direction => ray intersects triangle
//				System.out.println("t: "+t);
				return t;
			} else {
				// no intersection
				return Double.NaN;
			}
		}

	}

	public double[] getNormal(double[] p) {
		// TODO: calculate real normal. new the facetnormal is return
		double[] n = Arrays.copyOfRange(model.getFaceNormals(), facetNormalIndex*3, facetNormalIndex*3+3);
		if(!SMOOTH_SHADING) {
			double[] n1 = Arrays.copyOfRange(model.normals, normalIndices[0]*3, normalIndices[0]*3+3);
			double[] n2 = Arrays.copyOfRange(model.normals, normalIndices[1]*3, normalIndices[1]*3+3);
			double[] n3 = Arrays.copyOfRange(model.normals, normalIndices[2]*3, normalIndices[2]*3+3);
			double l1 = Geometry.norm(Geometry.difference(n1, p));
		}
		
		return n;
	}

	
	public Postion inHalfSpace(double d, int axis) {
		double[] v = model.getVertices();
		
		if (v[vertexIndices[0]*3+axis] >= d && v[vertexIndices[1]*3+axis] >= d && v[vertexIndices[2]*3+axis] >= d) {
			return Postion.NEAR;
		} else if (v[vertexIndices[0]*3+axis] < d && v[vertexIndices[1]*3+axis] < d && v[vertexIndices[2]*3+axis] < d) {
			return Postion.FAR;
		} else {
			return Postion.BOTH;
		}
		
		/*
		switch (axis) {
		case 0:
			// x
			if(vertices[vertexIndices[0]*3+0] >= m &&
			   vertices[vertexIndices[1]*3+0] >= m && 
			   vertices[vertexIndices[2]*3+0] >= m) {
				return Postion.NEAR;
			} else if(vertices[vertexIndices[0]*3+0] < m &&
					  vertices[vertexIndices[1]*3+0] < m && 
					  vertices[vertexIndices[2]*3+0] < m) {
				return Postion.FAR;
			} else {
				return Postion.BOTH;
			}
//			break;
		case 1:
			// y
			if(vertices[vertexIndices[0]*3+1] >= m &&
			   vertices[vertexIndices[1]*3+1] >= m && 
			   vertices[vertexIndices[2]*3+1] >= m) {
				return Postion.NEAR;
			} else if(vertices[vertexIndices[0]*3+1] < m &&
					  vertices[vertexIndices[1]*3+1] < m && 
					  vertices[vertexIndices[2]*3+1] < m) {
				return Postion.FAR;
			} else {
				return Postion.BOTH;
			}
//			break;
		case 2:
			// z
			if(vertices[vertexIndices[0]*3+2] >= m &&
			   vertices[vertexIndices[1]*3+2] >= m && 
			   vertices[vertexIndices[2]*3+2] >= m) {
				return Postion.NEAR;
			} else if(vertices[vertexIndices[0]*3+2] < m &&
					  vertices[vertexIndices[1]*3+2] < m && 
					  vertices[vertexIndices[2]*3+2] < m) {
				return Postion.FAR;
			} else {
				return Postion.BOTH;
			}
//			break;
		default:
			return Postion.BOTH;
		}
		*/
	}
	
	public double[] getPostion() {
		double[] vertices = model.getVertices();
		// calculate center of triangle
		double[] postion = new double[]{(vertices[vertexIndices[0]*3+0]+vertices[vertexIndices[1]*3+0]+vertices[vertexIndices[2]*3+0])/3,
				                        (vertices[vertexIndices[0]*3+1]+vertices[vertexIndices[1]*3+1]+vertices[vertexIndices[2]*3+1])/3,
				                        (vertices[vertexIndices[0]*3+2]+vertices[vertexIndices[1]*3+2]+vertices[vertexIndices[2]*3+2])/3};
		return postion;
	}
	
	/****************************************************************/
	/** Getter/Setter 												*/
	/****************************************************************/
	
	public ObjectModel getModel() {
		return model;
	}

	public void setModel(ObjectModel model) {
		this.model = model;
	}

	public int[] getVertexIndices() {
		return vertexIndices;
	}

	public void setVertexIndices(int[] vertexIndices) {
		this.vertexIndices = vertexIndices;
	}

	public int[] getNormalIndices() {
		return normalIndices;
	}

	public void setNormalIndices(int[] normalIndices) {
		this.normalIndices = normalIndices;
	}

	public int[] getTexcoordIndices() {
		return texcoordIndices;
	}

	public void setTexcoordIndices(int[] texcoordIndices) {
		this.texcoordIndices = texcoordIndices;
	}

	public int getFacetNormalIndex() {
		return facetNormalIndex;
	}

	public void setFacetNormalIndex(int facetNormalIndex) {
		this.facetNormalIndex = facetNormalIndex;
	}
}
